using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Timers;
using Timer = System.Timers.Timer;
using RegionInterface;
namespace RegionClient
{
    public class RegionTCP
    {
        public bool OnLine = false;
        public DictionarySync<int, PlayerData> PlayerList;
        public event MessageHandle OnMessage;
        public event Action<bool> OnPlayersLoad;
        public event Action<PlayerData> OnPlayerLogin;
        public event Action<string> OnPlayerLogout;
        public event LoginHandle OnLogin;
        public event LoginHandle OnLogout;
        public PlayerData Player;
        public ClientSocket client;
        public float ServerPing = 0;    
        //public static TaskReady taskList;
        public RegionTCP(string serverIp, int port, int background, string doMain)
        {
            client = new ClientSocket(serverIp, port);
            client.OnConnect += delegate(bool result, string errorMsg)
            {
                if (result)
                {
                    DataObject msg = client.BufferPool.Get();
                    msg.WriteByte(MessageType.LOGIN);
                    msg.WriteInt32(Player.PId);
                    msg.WriteString(Player.PNick);
                    msg.WriteString(Player.Para);
                    msg.WriteInt32(background);
                    msg.WriteString(doMain);

                    SendMessage(msg);
                    client.BufferPool.Put(msg);
                }
                else
                {
                    Close();
                    if (OnLogin != null)
                    {
                        OnLogin(false, errorMsg);
                    }
                }
            };
            client.OnDisConnect += delegate(Socket result)
            {
                Close();
                OnLogout(false, "Lost connection!");
            };
            client.OnMessage += client_OnMessage;
            PlayerList = new DictionarySync<int, PlayerData>();
            //taskList = new TaskReady();
        }
        public void Login(int id, string name, string para)
        {
            Console.WriteLine("id:" + id + ",name:" + name);
            OnLine = false;
            runLogin = true;
            Player = new PlayerData(id, name, para);
            PlayerList.Clear();
            //taskList.Init();
            client.ConnectServer();
        }
        public void RequestPlayers()
        {
            if (OnLine)
            {
                DataObject msg = client.BufferPool.Get();
                msg.WriteByte(MessageType.GETUSERS);
                msg.WriteInt32(Player.PId);

                SendMessage(msg);
                client.BufferPool.Put(msg);
            }
        }
        void client_OnMessage(DataObject msg)
        {
            byte type = msg.ReadByte();
            switch (type)
            {
                case MessageType.LOGIN_BACK:
                    {
                        string content = msg.ReadString();
                        if (string.Compare(content, "ok") == 0)
                        {
                            OnLine = true;
                            runLoginThread();
                            if (OnLogin != null)
                            {
                                OnLogin(true, "ok");
                            }
                        }
                        else
                        {
                            Close();
                            if (OnLogin != null)
                            {
                                OnLogin(false, content);
                            }
                        }
                        break;
                    }
                case MessageType.GETUSERS_BACK:
                    {
                        string content = msg.ReadString();
                        if (string.Compare(content, "ok") == 0)
                        {
                            if (OnPlayersLoad != null)
                            {
                                OnPlayersLoad(true);
                            }
                        }
                        break;
                    }
                case MessageType.LOGIN://somebody online
                    {
                        string content = msg.ReadString();
                        string[] players = content.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                        for (int i = 0; i < players.Length; i++)
                        {
                            string[] para = players[i].Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                            //string[] epoint = para[2].Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
                            int id = int.Parse(para[0]);
                            if (!PlayerList.ContainsKey(id))
                            {
                                PlayerData loginPlayer = new PlayerData(id, para[1], para[3]);
                                PlayerList[loginPlayer.PId] = loginPlayer;
                                onPlayerLogin(loginPlayer);
                            }
                        }
                        break;
                    }
                case MessageType.LOGOUT:
                    {
                        int logoutId = msg.ReadInt32();
                        PlayerData logoutPlayer = PlayerList[logoutId];
                        if (logoutPlayer != null)
                        {
                            PlayerList.Remove(logoutId);
                            onPlayerLogout(logoutId);
                        }
                        break;
                    }
                //case MessageType.GET_SERVER_TIME_BACK:
                //    {
                //        if (ackList.ContainsKey(msg.To))
                //        {
                //            ackList[msg.To].Enable = true;
                //            ackList[msg.To].Set(msg.Content);
                //        }
                //        break;
                //    }
                case MessageType.KEEPALIVE:
                    {
                        ServerPing = (DateTime.Now.Ticks - tickNow) / 10000000.0f;
                        break;
                    }
                default:
                    {
                        msg.Reset();
                        ReceiveMessage(msg);
                        break;
                    }
            }    
        }

        private bool runLogin = true;
        private string loginId = string.Empty;
        private string loginPlayerId = string.Empty;
        private string logoutPlayerId = string.Empty;
        private long logoutCount = 0;
        private Dictionary<string, PlayerData> tmpPlayersList = new Dictionary<string, PlayerData>();
        private Dictionary<string, bool> scenePlayersList = new Dictionary<string, bool>();
        private long tickNow = 0;
        private void runLoginThread()
        {
            scenePlayersList.Clear();
            tmpPlayersList.Clear();
            tickNow = 0;


            DataObject msg = client.BufferPool.Get();
            msg.WriteByte(MessageType.KEEPALIVE);
            msg.WriteInt32(Player.PId);

            Thread loginThread  = new Thread(delegate()
            {
                while (runLogin)
                {

                    
                    #region manager login or logout
                    bool needSleep = true;
                    bool loginHasLogout = false;
                    lock (loginPlayerId)
                    {
                        loginId = loginPlayerId;
                    }
                    if (string.IsNullOrEmpty(loginId))
                    {
                        PlayerData player = null;
                        lock (tmpPlayersList)
                        {
                            foreach (KeyValuePair<string, PlayerData> item in tmpPlayersList)
                            {
                                if (!scenePlayersList.ContainsKey(item.Key))
                                {
                                    player = item.Value;
                                    break;
                                }
                            }
                        }
                        if (player != null)
                        {
                            needSleep = false;
                            lock (loginPlayerId)
                            {
                                loginPlayerId = player.PId.ToString();
                            }
                            if (OnPlayerLogin != null)
                            {
                                OnPlayerLogin(player);
                                scenePlayersList[player.PId.ToString()] = true;
                            }
                        }
                    }
                    //if (Interlocked.Read(ref logoutCount) != 0)
                    //{
                        foreach (KeyValuePair<string, bool> item in scenePlayersList)
                        {
                            lock (tmpPlayersList)
                            {
                                if (!tmpPlayersList.ContainsKey(item.Key))
                                {
                                    if (string.Compare(item.Key, loginId) != 0)
                                    {
                                        logoutPlayerId = item.Key;
                                    }
                                    else
                                    {
                                        loginHasLogout = true;
                                    }
                                    break;
                                }
                            }
                        }
                        if (string.IsNullOrEmpty(logoutPlayerId) && !loginHasLogout)
                        {
                            long initialValue = 0;
                            do
                            {
                                initialValue = logoutCount;
                            } while (initialValue != Interlocked.CompareExchange(ref logoutCount, 0, initialValue));
                        }
                        else
                        {
                            needSleep = false;
                            if (OnPlayerLogout != null)
                            {
                                OnPlayerLogout(logoutPlayerId);
                                scenePlayersList.Remove(logoutPlayerId);
                            }
                            logoutPlayerId = string.Empty;
                            Interlocked.Decrement(ref logoutCount);
                        }
                    //}
                    if (needSleep)
                    {
                        Thread.Sleep(500);
                    }
                    #endregion
                    //continue;
                    #region keep login message
                    if (DateTime.Now.Ticks - tickNow > 100000000)//10s
                    {
                        tickNow = DateTime.Now.Ticks;
                        SendMessage(msg);
                    }
                    #endregion
                }
            });
            loginThread.IsBackground = true;
            loginThread.Start();
        }
        public void GetNextPlayer()
        {
            if (!OnLine) return;
            lock (loginPlayerId)
            {
                loginPlayerId = string.Empty;
            }
            //Console.WriteLine("------GetNextPlayer <<");      
        }
        void onPlayerLogin(PlayerData player)
        {
            if (!OnLine) return;
            lock (tmpPlayersList)
            {
                if (!tmpPlayersList.ContainsKey(player.PId.ToString()))
                {
                    tmpPlayersList[player.PId.ToString()] = player;
                }
            }
            //Console.WriteLine("------login player " + player.PId);        
        }
        void onPlayerLogout(int id)
        {
            lock (tmpPlayersList)
            {
                if (tmpPlayersList.ContainsKey(id.ToString()))
                {
                    tmpPlayersList.Remove(id.ToString());
                }
            }
            Interlocked.Increment(ref logoutCount);
        }

        void ReceiveMessage(DataObject msg)
        {
            if (OnMessage != null)
            {
                OnMessage(msg);
            }
        }
        //public void GetServerTime(Action<DateTime> serverTimeHandle)
        //{
        //    if (!OnLine) return;
        //    string tick = DateTime.Now.Ticks.ToString();
        //    DataObject msg = client.BufferPool.Get();//(Player.PId, tick, "time", "");
        //    msg.WriteByte(MessageType.GET_SERVER_TIME);
        //    msg.WriteString(tick);
        //    WaitAckHandle waitAck = new WaitAckHandle();
        //    ackList[tick] = waitAck;
        //    SendMessage(msg);
        //    client.BufferPool.Put(msg);
        //    waitAck.WaitOne(delegate(string result)
        //    {
        //        DateTime serverTime;
        //        if (DateTime.TryParse(result, out serverTime))
        //        {
        //            serverTimeHandle(serverTime);
        //        }
        //        //ackList.Remove(msg.Tick);
        //    });
        //}
        public void BroadcastMessage(string type, string content)
        {
            DataObject buffer = client.BufferPool.Get();
            buffer.WriteByte(MessageType.BROADCAST);
            buffer.WriteInt32(Player.PId);
            buffer.WriteString(type);
            buffer.WriteString(content);
            SendMessage(buffer);
            client.BufferPool.Put(buffer);
        }
        public void SendMessage(int to, string type, string content)
        {
            DataObject msg = client.BufferPool.Get();
            msg.WriteByte(MessageType.POINT_TO_POINT);
            msg.WriteInt32(Player.PId);
            msg.WriteInt32(to);
            msg.WriteString(type);
            msg.WriteString(content);

            SendMessage(msg);
            client.BufferPool.Put(msg);
        }
        public void Close()
        {
            OnLine = false;
            runLogin = false;
            try
            {
                if (client != null)
                {
                    client.CloseAndDispose();
                }
                //taskList.Dispose();
            }
            catch (Exception e)
            {
                addError(e.ToString());
            }
        }

        public void SendMessage(DataObject buffer)
        {
            try
            {
                client.Send(buffer);
            }
            catch (Exception e)
            {
                addError(e.ToString());
            }
        }
        void addError(string error)
        {
            //Console.WriteLine(error);
            if(ErrorHandle != null)
            {
                ErrorHandle(error);
            }
            //UnityEngine.Debug.Log(error);
        }
        public static Action<string> ErrorHandle;
    }
}
